#pragma once
#include"global_var.cpp"
#include"topological_ckt.cpp"
#include"write_result.cpp"

auto simulator = [](t_ckt& ckt,std::vector<fault*> faults, result* res ){
    ckt.fault_sim(patterns,faults);
    ckt.reset();
    res->write(faults);
};

class ThreadPool {
public:
    ThreadPool(size_t);
    void enqueue(std::vector<fault*>&, result* res);
    ~ThreadPool();
private:
    std::vector< std::thread > workers;
    std::queue< std::pair<std::vector<fault*>,result*> > tasks;
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop;
};

inline ThreadPool::ThreadPool(size_t threads)
    :   stop(false)
{
    for(size_t i = 0;i<threads;++i)
        workers.emplace_back(
            [this]
            {
                t_ckt ckt;
                for(;;)
                {
                    std::vector<fault*> faults;
                    result* res = nullptr;
                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        this->condition.wait(lock,
                            [this]{ return this->stop || !this->tasks.empty(); });
                        if(this->stop && this->tasks.empty())
                            return;

                        auto item = std::move(this->tasks.front());
                        this->tasks.pop();

                        res = item.second;
                        faults = std::move(item.first);
                    }
                    simulator(ckt,faults,res);
                }
            }
        );
}

void ThreadPool::enqueue(std::vector<fault*>& faults, result* res) 
{        
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        if(stop)
            throw std::runtime_error("enqueue on stopped ThreadPool");
        tasks.emplace(std::move(faults),res);
    }
    condition.notify_one();
}

inline ThreadPool::~ThreadPool()
{
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        stop = true;
    }
    condition.notify_all();
    for(std::thread &worker: workers)
        worker.join();
}
